using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;

namespace Slaks.Graphs.Graphs
{
    public class FactoryBase<T>
    {
        //private static FactoryBase<T> s_instance;
        private IEnumerable<T> _alogrithms;

        //protected static FactoryBase<T> Instance
        //{
        //    get
        //    {
        //        if (s_instance == null) s_instance = new FactoryBase<T>();
        //        return s_instance;
        //    }
        //}
        
        public T GetInstance(string agorithmName)
        {
            var algorithm = FindClass(agorithmName, GetAvailableAlogrithms());
            return algorithm;
        }

        protected virtual T FindClass(string key,IEnumerable<T> algorithms)
        {
            foreach (var algorithm in algorithms)
            {
                if (algorithm.GetType().Name.Equals(key))
                {
                    return algorithm;
                }

            }

            throw new Exception("No algorithm found");
        }

        protected static IEnumerable<T> GetAvailableAlogrithms()
        {
            Debug.WriteLine("Looking for algorithms of type: " + typeof(T));
            
            IList<T> result = new List<T>();
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            Debug.WriteLine("Found "+assemblies.Count()+ " assemblies in memory");
            Debug.Indent();
            foreach (Assembly assembly in assemblies)
            {
                Debug.WriteLine("processing assembly: " + assembly.FullName + " types");
                if (assembly.FullName != null)
                {
                    if (assembly.FullName.Contains("mscorlib") || assembly.FullName.Contains("System") ||
                        assembly.FullName.Contains("Gallio") || assembly.FullName.Contains("MbUnit"))
                    {
                        continue;
                    }
                }
            
            var types = assembly.GetTypes();
                Debug.WriteLine("Found " + types.Count() + " types in assembly");
                Debug.Indent();
                foreach (Type type in types)
                {
                    Debug.WriteLine("processing Type: " + type.FullName);
                    if (type.IsClass && type.IsSubclassOf(typeof(T)))
                    {
                        Debug.WriteLine("Type is OK");
                        ConstructorInfo ci = type.GetConstructor(new Type[] { });
                        if (ci != null)
                        {
                            var alg = (T) ci.Invoke(new object[] {});
                            result.Add(alg);
                            Debug.WriteLine("Type added");
                        }
                        else
                        {
                            Debug.WriteLine("Type doesn't have a constructor. Skipping.");
                        }
                    }
                }
                Debug.Unindent();
            }
            Debug.Unindent();
            Debug.WriteLine("Found "+result.Count+" applicable alogrithm types");
            return result;
        }

        public int Count
        {
            get
            {
                return AvailableAlgoirthms.Count();
            }
        }
        public IEnumerable<string> AvailableAlgoirthmsNames
        {
            get
            {
                return from algorithm in AvailableAlgoirthms
                select GetAlgorithmName(algorithm);
            }
        }
        public IEnumerable<T> AvailableAlgoirthms
        {
            get
            {
                if (_alogrithms == null)
                {
                    _alogrithms = GetAvailableAlogrithms();
                }
                return _alogrithms;
            }
        }
        protected virtual string GetAlgorithmName(T algorithm)
        {
            return algorithm.GetType().Name;
        }
    }
}